﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace BetterAzureTableStorage.Tests
{


    /// <summary>
    ///This is a test class for TTableStorageQueryTranslatorTest and is intended
    ///to contain all TTableStorageQueryTranslatorTest Unit Tests
    ///</summary>
    [TestClass()]
    public class TTableStorageQueryTranslatorTest
    {
        /// <summary>
        ///A test for Translate
        ///</summary>
        [TestMethod()]
        public void TranslateTest()
        {
            TTableStorageQueryTranslator target = new TTableStorageQueryTranslator();
            bool hasExcepted = false;

            //Test for error when passing in a non supported binary operation
            try
            {
                target.Translate(Expression.LeftShift(Expression.Constant(10, typeof(int)), Expression.Constant(2, typeof(int))), t =>
                {
                    return t.Name;
                }, null);
            }
            catch (Exception e)
            {
                Assert.AreEqual<Type>(typeof(NotSupportedException), e.GetType());
                Assert.AreEqual<string>("The binary operator 'LeftShift' is not supported", ((NotSupportedException)e).Message);
                hasExcepted = true;
            }

            Assert.IsTrue(hasExcepted);
            hasExcepted = false;

            //Test for error when passing in a null value for a constant
            try
            {
                target.Translate(Expression.Constant(null, typeof(int?)), t =>
                {
                    return t.Name;
                }, null);
            }
            catch (Exception e)
            {
                Assert.AreEqual<Type>(typeof(NotImplementedException), e.GetType());
                hasExcepted = true;
            }

            Assert.IsTrue(hasExcepted);
            hasExcepted = false;

            //Test for not queryable objects
            try
            {
                List<int> intList = new List<int>();
                target.Translate(Expression.Constant(intList), t =>
                {
                    return t.Name;
                }, null);
            }
            catch (Exception e)
            {
                Assert.AreEqual<Type>(typeof(NotSupportedException), e.GetType());
                Assert.AreEqual<string>("The constant for 'System.Collections.Generic.List`1[System.Int32]' is not supported", ((NotSupportedException)e).Message);
                hasExcepted = true;
            }
            Assert.IsTrue(hasExcepted);
            hasExcepted = false;
        }

        /// <summary>
        /// Test the translate method using the default rename property, set by the getter in the TTableStorageQuery
        /// </summary>
        /// [TestMethod]
        public void TranslateUsingDefaultRenamePropertyTest()
        {
            TTableStorageQuery<GenericParameterHelper> target = new TTableStorageQuery<GenericParameterHelper>(null, null, null, null);
        }

        private static List<Car> Cars = new List<Car>()
        {
            new Car() { ID = Guid.NewGuid(), MaxSpeed = 120, NeedsOilChange = false, PassengerLimit = 4, ModelName="Ford" },
            new Car() { ID = Guid.NewGuid(), MaxSpeed = 175, NeedsOilChange = false, PassengerLimit = 2, ModelName = "Chevy" },
            new Car() { ID = Guid.NewGuid(), MaxSpeed = 125, NeedsOilChange = false, PassengerLimit = 6 },
            new Car() { ID = Guid.NewGuid(), MaxSpeed = 220, NeedsOilChange = false, PassengerLimit = 1 },
            new Car() { ID = Guid.NewGuid(), MaxSpeed = 120, NeedsOilChange = true, PassengerLimit = 2 },
            new Car() { ID = Guid.NewGuid(), MaxSpeed = 130, NeedsOilChange = true, PassengerLimit = 5 },
            new Car() { ID = Guid.NewGuid(), MaxSpeed = 110, NeedsOilChange = true, PassengerLimit = 6 },
        };

        [TestMethod]
        public void OneWhereConditionTest()
        {
            TTableStorageQueryTranslator translator = new TTableStorageQueryTranslator();
            IQueryable<Car> qry = CarsQry.Where(c => c.NeedsOilChange == false);
            string expected = "()?$filter=(NeedsOilChange%20eq%20false)";
            translator.Translate(qry.Expression, t => { return t.Name; }, null);
            Assert.AreEqual(expected, translator.TranslatedText);

            translator = new TTableStorageQueryTranslator();
            qry = CarsQry.Where(c => c.NeedsOilChange == false && c.PassengerLimit > 2);
            expected = "()?$filter=((NeedsOilChange%20eq%20false)%20and%20(PassengerLimit%20gt%202))";
            translator.Translate(qry.Expression, t => { return t.Name; }, null);
            Assert.AreEqual(expected, translator.TranslatedText);
        }

        [TestMethod]
        public void TwoWhereConditionTest()
        {
            TTableStorageQueryTranslator translator = new TTableStorageQueryTranslator();

            IQueryable<Car> qry = CarsQry.Where(c => c.NeedsOilChange == false).Where(c => c.PassengerLimit > 2);
            string expected = "()?$filter=(NeedsOilChange%20eq%20false)%20and%20(PassengerLimit%20gt%202)";

            translator.Translate(qry.Expression, t => { return t.Name; }, null);
            Assert.AreEqual(expected, translator.TranslatedText);

            // here's a translator with lots of test cases, maybe it will be useful for reference
            // https://bitbucket.org/jjrdk/linq2rest/src/594ecc4338cd/Linq2Rest/Provider/ExpressionProcessor.cs
        }

        [TestMethod]
        public void StringToLowerTest()
        {
            TTableStorageQueryTranslator translator = new TTableStorageQueryTranslator();
            string targetModelName = "Ford";
            IQueryable<Car> qry = CarsQry.Where(c => c.ModelName.ToLower() == targetModelName.ToLower());
            string expected = "()?$filter=(tolower(ModelName)%20eq%20tolower('Ford'))";

            translator.Translate(qry.Expression, t => t.Name, null);
            Assert.AreEqual(expected, translator.TranslatedText);
        }

        [TestMethod]
        public void StringToUpperTest()
        {
            TTableStorageQueryTranslator translator = new TTableStorageQueryTranslator();
            string targetModelName = "Ford";
            IQueryable<Car> qry = CarsQry.Where(c => c.ModelName.ToUpper() == targetModelName.ToUpper());
            string expected = "()?$filter=(toupper(ModelName)%20eq%20toupper('Ford'))";

            translator.Translate(qry.Expression, t => t.Name, null);
            Assert.AreEqual(expected, translator.TranslatedText);
        }

        [TestMethod]
        public void StringTrimTest()
        {
            TTableStorageQueryTranslator translator = new TTableStorageQueryTranslator();
            string targetModelName = "Ford";
            IQueryable<Car> qry = CarsQry.Where(c => c.ModelName.Trim() == targetModelName.Trim());
            string expected = "()?$filter=(trim(ModelName)%20eq%20trim('Ford'))";

            translator.Translate(qry.Expression, t => t.Name, null);
            Assert.AreEqual(expected, translator.TranslatedText);
        }

        [TestMethod]
        public void FirstOrDefaultTest()
        {
            //al.GetQuery<TStation>().Where(s =>
            //                s.BusinessID == processMessage.BusinessID &&
            //                s.Name.ToLower() == station.ToLower() &&
            //                s.IsActive == true).FirstOrDefault()
            TTableStorageQueryTranslator translator = new TTableStorageQueryTranslator();
            Car target = Cars[0];
            IQueryable<Car> qry = CarsQry.Where(c =>
                        c.ID == target.ID &&
                        c.ModelName.ToLower() == target.ModelName.ToLower() &&
                        c.NeedsOilChange == target.NeedsOilChange);
            translator.Translate(qry.Expression, t => t.Name, null);
        }

        private static IQueryable<Car> CarsQry = Cars.AsQueryable();
    }

    class Car
    {
        public Guid ID { get; set; }

        public int PassengerLimit { get; set; }

        public bool NeedsOilChange { get; set; }

        public int MaxSpeed { get; set; }

        public string ModelName { get; set; }
    }
}
